package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.seckill.dto.SeckillOrderAddDTO;
import cn.tedu.mall.pojo.seckill.model.Success;
import cn.tedu.mall.pojo.seckill.vo.SeckillCommitVO;
import cn.tedu.mall.seckill.config.RabbitMqComponentConfiguration;
import cn.tedu.mall.seckill.service.ISeckillService;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class SeckillServiceImpl implements ISeckillService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @DubboReference
    private IOmsOrderService dubboOmsOrderService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public SeckillCommitVO commitSeckill(SeckillOrderAddDTO seckillOrderAddDTO) {
        Long skuId = seckillOrderAddDTO.getSeckillOrderItemAddDTO().getSkuId();
        Long userId = getUserId();
        String reseckillCheckKey = SeckillCacheUtils.getReseckillCheckKey(skuId, userId);
        Long seckillTimes = stringRedisTemplate.boundValueOps(reseckillCheckKey)
                .increment();
        if(seckillTimes>1){
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN,
                    "商品已购！");
        }
        String stockKey = SeckillCacheUtils.getStockKey(skuId);
        if(!stringRedisTemplate.hasKey(stockKey)){
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,
                    "缓存中无库存信息！");
        }
        Long leftStock = stringRedisTemplate.boundValueOps(stockKey).decrement();
        if(leftStock<0){
            stringRedisTemplate.boundValueOps(reseckillCheckKey).decrement();
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您购买的商品已售罄");
        }
        OrderAddDTO orderAddDTO = converSeckillOrderToOrder(seckillOrderAddDTO);
        orderAddDTO.setUserId(userId);
        OrderAddVO orderAddVO = dubboOmsOrderService.addOrder(orderAddDTO);
        log.info("秒杀定单来了");
        // 第三阶段:使用消息队列(RabbitMQ)将秒杀成功记录信息保存到success表中
        Success success = new Success();
        BeanUtils.copyProperties(seckillOrderAddDTO.getSeckillOrderItemAddDTO(),
                success);
        success.setUserId(userId);
        success.setSeckillPrice(seckillOrderAddDTO.getSeckillOrderItemAddDTO().getPrice());
        success.setOrderSn(orderAddVO.getSn());
        rabbitTemplate.convertAndSend(
                RabbitMqComponentConfiguration.SECKILL_EX,
                RabbitMqComponentConfiguration.SECKILL_RK,
                success
        );
        // 第四阶段：秒杀信息返回
        SeckillCommitVO seckillCommitVO = new SeckillCommitVO();
        BeanUtils.copyProperties(orderAddVO, seckillCommitVO);
        return seckillCommitVO;
    }

    /**
     * 将秒杀订单信息转换为普通订单信息
     * @param seckillOrderAddDTO 秒杀订单信息
     * @return 普通订单信息
     */
    private OrderAddDTO converSeckillOrderToOrder(SeckillOrderAddDTO seckillOrderAddDTO) {
        OrderAddDTO orderAddDTO = new OrderAddDTO();
        BeanUtils.copyProperties(seckillOrderAddDTO, orderAddDTO);

        List<OrderItemAddDTO> list = new ArrayList<>();
        OrderItemAddDTO orderItemAddDTO = new OrderItemAddDTO();
        BeanUtils.copyProperties(seckillOrderAddDTO.getSeckillOrderItemAddDTO(), orderItemAddDTO);
        list.add(orderItemAddDTO);

        orderAddDTO.setOrderItems(list);
        return orderAddDTO;
    }


    public CsmallAuthenticationInfo getUserInfo(){
        // 编写获取SpringSecurity上下文的代码
        UsernamePasswordAuthenticationToken authenticationToken=
                (UsernamePasswordAuthenticationToken)
                        SecurityContextHolder.getContext().getAuthentication();
        // 为了逻辑严谨,判断一下SpringSecurity获取的用户信息是否为null
        if(authenticationToken == null){
            throw new CoolSharkServiceException(
                    ResponseCode.UNAUTHORIZED,"您没有登录");
        }
        // 从SpringSecurity上下文中获取用户信息
        CsmallAuthenticationInfo csmallAuthenticationInfo=
                (CsmallAuthenticationInfo)
                        authenticationToken.getCredentials();
        // 最后别忘了将用户信息返回
        return csmallAuthenticationInfo;
    }

    //  业务逻辑层中实际需求都是获取用户的id
    // 我们再一个方法,直接返回用户id,方便业务调用
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
